//
//  NSObject+AvoidCrash.m
//  TTCrash
//
//  Created by 陶建 on 2017/4/11.
//  Copyright © 2017年 taoJ. All rights reserved.
//

#import "NSObject+AvoidCrash.h"
#import <objc/runtime.h>

#ifdef DEBUG
#define TTLog(...)                      NSLog(__VA_ARGS__);
#else
#define TTLog(...)
#endif

/*************************************** runtime ***************************************/
#pragma mark - NSObject
@implementation NSObject (Swizzle)

/** 方法交换 */
+ (void)tt_swizzleInstanceMethod:(Class)class originSelector:(SEL)originSelector idealSelector:(SEL)idealSelector {
    Method oSelector = class_getInstanceMethod(class, originSelector);
    Method iSelector = class_getInstanceMethod(class, idealSelector);
#ifdef DEBUG
    // 不存在方法
    if (!oSelector) {
        class_addMethod(class, originSelector, method_getImplementation(iSelector), method_getTypeEncoding(iSelector));
    }
    method_exchangeImplementations(iSelector, oSelector);
#else
    if (!oSelector) {
        class_addMethod(class, originSelector, method_getImplementation(iSelector), method_getTypeEncoding(iSelector));
    }
    method_exchangeImplementations(iSelector, oSelector);
#endif
}

+ (void)tt_swizzleClassMethod:(Class)class originSelector:(SEL)originSelector idealSelector:(SEL)idealSelector {
    Method oSelector = class_getClassMethod(class, originSelector);
    Method iSelector = class_getClassMethod(class, idealSelector);
#ifdef DEBUG
    if (!oSelector) {
        class_addMethod(class, originSelector, method_getImplementation(iSelector), method_getTypeEncoding(iSelector));
    }
    method_exchangeImplementations(iSelector, oSelector);
#else
    if (!oSelector) {
        class_addMethod(class, originSelector, method_getImplementation(iSelector), method_getTypeEncoding(iSelector));
    }
    method_exchangeImplementations(iSelector, oSelector);
#endif
    
}

@end

#pragma mark - NSString
@implementation NSString (AvoidCrash)

+ (void)load {
    [self tt_swizzleInstanceMethod:NSClassFromString(@"__NSCFConstantString") originSelector:@selector(substringFromIndex:) idealSelector:@selector(tt_substringFromIndex:)];
    [self tt_swizzleInstanceMethod:NSClassFromString(@"__NSCFConstantString") originSelector:@selector(substringToIndex:) idealSelector:@selector(tt_substringToIndex:)];
    [self tt_swizzleInstanceMethod:NSClassFromString(@"__NSCFConstantString") originSelector:@selector(substringWithRange:) idealSelector:@selector(tt_substringWithRange:)];
}

- (instancetype)tt_substringFromIndex:(NSUInteger)index {
    if (index > self.length) {
        TTLog(@"Index %zd out of bounds; string length %zd", index, self.length);
        return @"";
    }
    return [self tt_substringFromIndex:index];
}


- (instancetype)tt_substringToIndex:(NSUInteger)index {
    if (index > self.length) {
        TTLog(@"Index %zd out of bounds; string length %zd", index, self.length);
        index = self.length;
    }
    return [self tt_substringToIndex:index];
}

- (instancetype)tt_substringWithRange:(NSRange)range {
    NSUInteger location = range.location;
    NSUInteger length = range.length;
    if (location > self.length) {
        TTLog(@"Index %zd out of bounds; string length %zd", location, self.length);
        return @"";
    }
    if (location + length > self.length) {
        TTLog(@"Range %@ out of bounds; string length %zd", NSStringFromRange(range), self.length);
        return [self tt_substringWithRange:NSMakeRange(location, self.length-location)];
    }
    
    return [self tt_substringWithRange:range];
}

@end

#pragma mark - NSArray
@implementation NSArray (AvoidCrash)
/**
 但是不同的创建数组的方法导致不同的类簇（其实也就是不同的指针），下面我们就看
 NSArray *arr1 =  @[@"1",@"2"]; __NSArrayI
 NSArray *arr2 =  [[NSArray alloc]init]; __NSArray0
 NSArray *arr3 =  [[NSArray alloc] initWithObjects: @"1",nil]; __NSSingleObjectArrayI
 NSArray *arr4 =  [NSArray alloc]; __NSPlaceholderArray
 */
+ (void)load {
    [self tt_swizzleInstanceMethod:NSClassFromString(@"__NSArrayI") originSelector:@selector(objectAtIndex:) idealSelector:@selector(tt_objectAtIndex:)];
    
    [self tt_swizzleInstanceMethod:NSClassFromString(@"__NSArrayI") originSelector:@selector(addObject:) idealSelector:@selector(tt_addObject:)];
}

- (id)tt_objectAtIndex:(NSUInteger)index {
    if (index < self.count && self.count > 0) {
        return [self tt_objectAtIndex:index];
    }
    
    TTLog(@"%@ --> index = %lu时越界!", [self class], index);
    return nil;
}

- (id)tt_addObject:(id)object {
    NSLog(@"'NSArray' not add object");
    return self;
}

@end

@implementation NSMutableArray (AvoidCrash)

+ (void)load {
    [self tt_swizzleInstanceMethod:NSClassFromString(@"__NSArrayM") originSelector:@selector(addObject:) idealSelector:@selector(tt_addObject:)];
    
    [self tt_swizzleInstanceMethod:NSClassFromString(@"__NSArrayM") originSelector:@selector(objectAtIndex:) idealSelector:@selector(tt_objectAtIndex:)];
    
    [self tt_swizzleInstanceMethod:NSClassFromString(@"__NSArrayM") originSelector:@selector(removeObjectAtIndex:) idealSelector:@selector(tt_removeObjectAtIndex:)];
}

- (void)tt_removeObjectAtIndex:(NSUInteger)index {
    if (index < self.count) {
        [self tt_removeObjectAtIndex:index];
    }
    
    TTLog(@"range {%zd, 1} extends beyond bounds for empty array", index);
}

- (void)tt_addObject:(id)object {
    if (object != nil) {
        [self tt_addObject:object];
        return;
    }
    
    TTLog(@"added object cannot be nil");
}

- (id)tt_objectAtIndex:(NSUInteger)index {
    if (index < self.count) {
        return [self tt_objectAtIndex:index];
    }
    
    TTLog(@"%@ ---> index = %lu越界", [self class], index);
    return nil;
}

@end

#pragma mark - NSDictionary
@implementation NSDictionary (AvoidCrash)

+ (void)load {
    [self tt_swizzleClassMethod:[self class] originSelector:@selector(dictionaryWithObjects:forKeys:count:) idealSelector:@selector(tt_dictionaryWithObjects:forKeys:count:)];
}

+ (instancetype)tt_dictionaryWithObjects:(const id  _Nonnull __unsafe_unretained *)objects forKeys:(const id<NSCopying>  _Nonnull __unsafe_unretained *)keys count:(NSUInteger)cnt {
    id instance = nil;
    
    @try {
        instance = [self tt_dictionaryWithObjects:objects forKeys:keys count:cnt];
    }
    @catch (NSException *exception) {
        //处理错误的数据，然后重新初始化一个字典
        NSUInteger index = 0;
        id  _Nonnull __unsafe_unretained newObjects[cnt];
        id  _Nonnull __unsafe_unretained newkeys[cnt];
        // 过滤nil
        for (int i = 0; i < cnt; i++) {
            if (objects[i] && keys[i]) {
                newObjects[index] = objects[i];
                newkeys[index] = keys[i];
                index++;
            }
        }
        instance = [self tt_dictionaryWithObjects:newObjects forKeys:newkeys count:index];
    }
    @finally { // 最后都会走
        return instance;
    }
}

@end
